package com.ihakul.logger;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.logging.ErrorManager;
import java.util.logging.Filter;
import java.util.logging.Formatter;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;

/**
 * 拦截严重错误{@link Level#SEVERE SEVERE}级别的及崩溃日志，除了输出到日志文件中以外，如果初始化{@link LogBuilder}时还配置
 * 了{@link SevereReporter SevereReporter}'s，还会通过其导到指定的目的地，如网络上的某个位置，邮件服务器等。
 *
 * @author Aaron
 * @version 2.1
 * Create at 2019/7/16
 */
final class RecordFilter extends ErrorManager implements Filter {

    private final Formatter mFormatter;
    private LowHandler mLowHandler;

    /**
     * The Reporter
     */
    private SevereReporter mReporter;


    /** Constructor, See {@link RecordFilter}  */
    RecordFilter(Formatter formatter){
        mFormatter = formatter;
    }

    private String format(LogRecord lr) {
        Throwable thrown = lr.getThrown();
        if (thrown != null) {
            StringWriter sw = new StringWriter();
            PrintWriter pw = new PrintWriter(sw);
            sw.write(mFormatter.format(lr));
            sw.write("\n");
            thrown.printStackTrace(pw);
            pw.flush();
            return sw.toString();
        } else {
            return mFormatter.format(lr);
        }
    }

    boolean put(Throwable error) {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        pw.println();
        error.printStackTrace(pw);
        pw.flush();

        String err=sw.toString();
        if(submitFailure(err))
        {
            dump(new LogRecord(Level.SEVERE, err));
            return false;
        }

        return true;
    }

    void addReporter(SevereReporter reporter) {
        mReporter=reporter;
    }

    void addLowHandler(LowHandler handler){
        mLowHandler=handler;
    }

    private boolean submitFailure(String error) {
        boolean acc = mReporter!=null;
        if (!acc){
            return true;
        }

        return !mReporter.report(error);
    }

    private void dump(LogRecord err){
        err.setLoggerName("Oops !!!");
        Logger root=IotLogger.rootLogger();
        Handler[] handlers=root.getHandlers();

        if (handlers.length > 1)
        {
            for (Handler h : handlers) {
                if(h instanceof BufferedFileHandler){

                    /* write and flush */
                    h.publish(err);
                    ((BufferedFileHandler)h).sureFlush();
                    break;
                }
            }
        }
    }

    @Override
    public boolean isLoggable(LogRecord lr) {
        if (Level.SEVERE.equals(lr.getLevel())) {
            return submitFailure(format(lr));
        }

        else if(mLowHandler != null &&
          mLowHandler.isLoggable(lr)){
            mLowHandler.publish(lr);
        }

        // The content of the files is consistent with the logcat.
        return true;
    }


    /**  Errors that need to be reported  ***/
    @SuppressWarnings("unused")
    private class ReportErrors extends Throwable {
        private static final String Self="com.ihakul.logger.RecordFilter";
        private static final String Internal="java.util.logging.";

        private ReportErrors(String message, Throwable cause) {
            super(message, cause, false, true);
            StackTraceElement[] trace = getStackTrace();

            int count=0; // 去掉顶部无参考价值的 StackTrace Info
            for (StackTraceElement traceElement : trace)
            {
                if(traceElement!=null&&(
                        traceElement.getClassName().equals(Self)||traceElement.getClassName().startsWith(Internal)
                        )){
                    count++;
                    continue;
                }

                setStackTrace(Arrays.copyOfRange(trace,count,trace.length));
                break;
            }
        }
    }

}
